﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows.Data;
using ScrumTable.BL.DM.DataManagement;
using Microsoft.Practices.Unity;
using ScrumTable.Common;
using ScrumTable.Common.Collections;
using ScrumTable.Common.EventHandling;
using ScrumTable.UI.ViewModel.Data;
using ScrumTable.UI.ViewModel.Extension;

#endregion

namespace ScrumTable.UI.ViewModel.View
{
    /// <summary>
    /// Description of the class
    /// </summary>
    public class SprintPlanningTaskViewModel : ViewModelBase
    {

        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------
        private bool _isMemberShow;
        private ObservableCollection<VelocityIterationData> _velocity;

        private IScrumItemViewModel<DomainBase> _selectedUserStoryIteration;
        private IScrumItemViewModel<DomainBase> _selectedUserStoryProject;
        private IScrumItemViewModel<DomainBase> _selectedUserStory;
        private Iteration _selectedIteration;
        #endregion
        #region Collections

        private ObservableCollection<IScrumItemViewModel<DomainBase>> _storiesOpen;
        private ObservableCollection<IScrumItemViewModel<DomainBase>> _storiesIteration;
        
        private ICollectionView[] _collectionViews = new ICollectionView[2]; 
        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        private ScrumViewModelData _scrumData;
        /// <summary>
        /// Gets or sets the scrum data.
        /// </summary>
        /// <value>The scrum data.</value>
        [Dependency]
        public ScrumViewModelData ScrumData
        {
            get
            {
                return _scrumData;
            }
            set
            {
                _scrumData = value;
                if (value.CurrentProject == null)
                    return;
               
                
                ScrumData.DataContext.ElementChanged += OnDataContextElementChanged;
                ScrumData.DataContext.CollectionChanged += OnDataContextCollectionChanged;
                ScrumData.PropertyChanged += OnScrumDataPropertyChanged;
                _collectionViews[0] = CollectionViewSource.GetDefaultView(StoriesOpen);
                _collectionViews[1] = CollectionViewSource.GetDefaultView(StoriesIteration);
                
                _collectionViews[0].SortDescriptions.Add(new SortDescription("Ranking", ScrumData.ConfigManager.Config.IsStackRankDescending?ListSortDirection.Descending:ListSortDirection.Ascending));
                _collectionViews[1].SortDescriptions.Add(new SortDescription("Ranking", ScrumData.ConfigManager.Config.IsStackRankDescending ? ListSortDirection.Descending : ListSortDirection.Ascending));
                
                
                UpdateTotalWork();
            }
        }

        /// <summary>
        /// Gets the available priorities.
        /// </summary>
        /// <value>The available priorities.</value>
        public double[] Priorities
        {
            get
            {
                return new double[] { 1, 2, 3, 4 };
            }
        }


        public Iteration SelectedIteration
        {
            get
            {
                if(_selectedIteration == null)
                {
                    _selectedIteration = ScrumData.CurrentIteration;
                }
                return _selectedIteration;
            }
            set
            {
                _selectedIteration = value;
                var item = Velocity.FirstOrDefault(x => x.IsSelected);
                if (item != null)
                {
                    item.IsSelected = false;
                }
                item = Velocity.FirstOrDefault(x => x.Iteration == value);
                if (item != null)
                {
                    item.IsSelected = true;
                }
                SendPropertyChanged("SelectedIteration");
                SendPropertyChanged("StoriesIteration");
            }
        }

        
        public ObservableCollection<VelocityIterationData> Velocity
        {
            get
            {
                var list = VelocityIterationData.CreateVelocityDataForProject(ElementManager, ScrumData, SelectedIteration);
                if (_velocity == null)
                {
                    _velocity = new ObservableCollection<VelocityIterationData>(list);
                }
                else
                {
                    foreach (var iterationData in _velocity)
                    {
                        iterationData.SetValues(list.FirstOrDefault(x => x.Name == iterationData.Name));
                    }
                }
                return _velocity;
            }
        }
        
        public bool IsMemberShow
        {
            get { return _isMemberShow; }
            set { _isMemberShow = value; SendPropertyChanged("IsMemberShow"); }
        }

        /// <summary>
        /// Gets the stories assigend to the project (Product Backlog).
        /// </summary>
        /// <value>The stories not assigned to a iteration</value>
        public ObservableCollection<IScrumItemViewModel<DomainBase>> StoriesOpen
        {
            get
            {

                var project = ScrumData.CurrentProject;
                if (project != null)
                {
                    return SetUserStoriesOnList(GetStoryBugItems(project.Stories,project.Bugs), ref _storiesOpen);
                }
                return null;
            }
        }


        /// <summary>
        /// Gets the stories of the current iteration.
        /// </summary>
        /// <value>The stories of current iteration.</value>
        public ObservableCollection<IScrumItemViewModel<DomainBase>> StoriesIteration
        {
            get
            {
                if (SelectedIteration != null)
                {
                    return SetUserStoriesOnList(GetStoryBugItems(SelectedIteration.Stories, SelectedIteration.Bugs), ref _storiesIteration);
                }
                return null;
            }
        }




        /// <summary>
        /// Gets or sets the selected user story.
        /// </summary>
        /// <value>The selected user story.</value>
        public IScrumItemViewModel<DomainBase> SelectedUserStory
        {
            get
            {
                return _selectedUserStory;
            }
            set
            {
                if (_selectedUserStory!=null)
                {
                    _selectedUserStory.IsPlacedOnScrumPoker = false;
                }
                
                if(value == null && _selectedUserStory != null)
                { 
                    //if the item is stil in one of both container -> dont deselect
                    IScrumItemViewModel<DomainBase> item;
                    if ((item = GetItemInContainer(StoriesIteration, _selectedUserStory)) != null) { SelectedUserStoryIteration = item; return; }
                    if ((item = GetItemInContainer(StoriesOpen, _selectedUserStory)) != null) { SelectedUserStoryProject = item; return; }
                }
                _selectedUserStory = value;
                if (_selectedUserStory != null)
                {
                    _selectedUserStory.IsPlacedOnScrumPoker = true;
                }
                SendPropertyChanged("SelectedUserStory");
                SendPropertyChanged("Tasks");
            }
        }

       /// <summary>
        /// Gets or sets the selected user story.
        /// </summary>
        /// <value>The selected user story.</value>
        public IScrumItemViewModel<DomainBase> SelectedUserStoryIteration
        {
            get { return _selectedUserStoryIteration; }
            set
            {
                _selectedUserStoryIteration = value;
                _selectedUserStoryProject = null;
                SelectedUserStory = value;
                SendPropertyChanged("SelectedUserStoryIteration");
                SendPropertyChanged("SelectedUserStoryProject");
            }
        }
        
        /// <summary>
        /// Gets or sets the selected user story.
        /// </summary>
        /// <value>The selected user story.</value>
        public IScrumItemViewModel<DomainBase> SelectedUserStoryProject
        {
            get { return _selectedUserStoryProject; }
            set
            {
                _selectedUserStoryProject = value;
                _selectedUserStoryIteration = null;
                SelectedUserStory = value;
                SendPropertyChanged("SelectedUserStoryProject");
                SendPropertyChanged("SelectedUserStoryIteration");
            }
        }


        /// <summary>
        /// Gets or sets the total work estimate over all tasks.
        /// </summary>
        /// <value>The total work estimate over all tasks.</value>
        public double TotalWorkEstimate { get; set; }
        /// <summary>
        /// Gets or sets the total work completed over all tasks.
        /// </summary>
        /// <value>The total work completed over all tasks.</value>
        public double TotalWorkCompleted { get; set; }
        /// <summary>
        /// Gets or sets the total work remaining over all tasks.
        /// </summary>
        /// <value>The total work remaining over all tasks.</value>
        public double TotalWorkRemaining { get; set; }

        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Initializes a new instance of the <see cref="SprintPlanningTaskViewModel"/> class.
        /// </summary>
        public SprintPlanningTaskViewModel()
        {
           
        }

        /// <summary>
        /// Releases managed (c# or .net) resources.
        /// </summary>
        protected override void ReleaseManagedResources()
        {
            ScrumData.DataContext.ElementChanged -= OnDataContextElementChanged;
            ScrumData.DataContext.CollectionChanged -= OnDataContextCollectionChanged;
            ScrumData.PropertyChanged -= OnScrumDataPropertyChanged;

            foreach (var story in _storiesIteration)
            {
                story.Dispose();
            }
            foreach (var story in _storiesOpen)
            {
                story.Dispose();
            }

            foreach (var x in _collectionViews)
            {
             x.SortDescriptions.Clear();   
            }
            for (int index = 0; index < _collectionViews.Length; index++)
            {
                _collectionViews[index].SortDescriptions.Clear();
                _collectionViews[index] = null;
            }
            base.ReleaseManagedResources();
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------


        public IScrumItemViewModel<DomainBase> GetItemInContainer(ObservableCollection<IScrumItemViewModel<DomainBase>> itemContainer, IScrumItemViewModel<DomainBase> item)
        {
            var storiesContainsIndex = itemContainer.IndexOf(item);
            if (storiesContainsIndex != -1)
            {
                _selectedUserStory = itemContainer.ElementAt(storiesContainsIndex);
                _selectedUserStory.IsPlacedOnScrumPoker = true;
                return _selectedUserStory;
            }
            return null;
        }

        /// <summary>
        /// Moves the items in the current list. 
        /// </summary>
        /// <param name="items"></param>
        /// <param name="listUserStories"></param>
        /// <returns></returns>
        private ObservableCollection<IScrumItemViewModel<DomainBase>> SetUserStoriesOnList(ObservableCollection<IScrumItemViewModel<DomainBase>> items, ref ObservableCollection<IScrumItemViewModel<DomainBase>> listUserStories)
        {
            if (listUserStories == null)
            {
                listUserStories = items;
            }
            else
            {
                listUserStories.UnionToLeft(items);
            }
            return listUserStories;
        }

        private ObservableCollection<IScrumItemViewModel<DomainBase>> GetStoryBugItems(DomainCollection<UserStory> story, DomainCollection<Bug> bug)
        {
            var stories = from x in story
                          where !ElementManager.IsClosedState(x.State) && _scrumData.ContainsFilter(x)
                          select (IScrumItemViewModel<DomainBase>)new UserStoryViewModel(x);

            ObservableCollection<IScrumItemViewModel<DomainBase>> userStories = new ObservableCollection<IScrumItemViewModel<DomainBase>>(stories);

            if (_scrumData.ConfigManager.Config.IsBugUserStory)
            {
                var bugs = from x in bug
                           where !ElementManager.IsClosedState(x.State) && _scrumData.ContainsFilter(x)
                           select (IScrumItemViewModel<DomainBase>)new BugViewModel(x);
                userStories = new ObservableCollection<IScrumItemViewModel<DomainBase>>(userStories.Union(bugs));
            }
            return userStories;
        }


 

        private void UpdateTotalWork()
        {
            TotalWorkEstimate = 0;
            TotalWorkCompleted = 0;
            TotalWorkRemaining = 0;

            foreach (Task task in ScrumData.CurrentIteration.Stories.SelectMany(story => story.Tasks))
            {
                TotalWorkEstimate += task.Estimate.GetValueOrDefault(0);
                TotalWorkCompleted += task.CompletedWork.GetValueOrDefault(0);
                TotalWorkRemaining += task.RemainingWork.GetValueOrDefault(0);
            }
            SendPropertyChanged("TotalWorkEstimate");
            SendPropertyChanged("TotalWorkCompleted");
            SendPropertyChanged("TotalWorkRemaining");
        } 
       
        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------


        private void OnScrumDataPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Filter")
            {
                SendPropertyChanged("StoriesIteration");
                SendPropertyChanged("StoriesOpen");
            }
        }

        void OnDataContextCollectionChanged(object sender, DomainCollectionChangedEventArgs e)
        {
            SendPropertyChanged("StoriesIteration");
            SendPropertyChanged("StoriesOpen");
            SendPropertyChanged("Velocity");
            UpdateTotalWork();
        }


        void OnDataContextElementChanged(object sender, PropertyChangedEventArgs e)
        {
            UpdateTotalWork();
            var args = e as DomainChangedEventArgs;
            if (args != null)
            {
                if ((args.ContainsType<UserStory>() || args.ContainsType<Bug>()) &&
                    args.ContainsPropertyName("Ranking"))
                {
                    if (args.ContainsType<Iteration>(true))
                    {
                        StoriesIteration.SortDomainItems();
                    }
                    else if (args.ContainsType<Project>(true))
                    {
                        StoriesOpen.SortDomainItems();
                    }
                }

                //TODO faster if the first two checks are removed?
                if ((args.ContainsType<UserStory>() || args.ContainsType<Bug>()) && args.ContainsPropertyNames("StoryPoints", "State"))
                {
                    SendPropertyChanged("StoriesIteration");
                    SendPropertyChanged("StoriesOpen");
                    SendPropertyChanged("Velocity");
                }
            }
        }

        #endregion

    
    }
}